package cn.edu.hitsz.compiler.parser;

import cn.edu.hitsz.compiler.NotImplementedException;
import cn.edu.hitsz.compiler.lexer.Token;
import cn.edu.hitsz.compiler.parser.table.*;
import cn.edu.hitsz.compiler.symtab.SymbolTable;

import java.util.ArrayList;
import java.util.List;

//TODO: 实验二: 实现 LR 语法分析驱动程序

/**
 * LR 语法分析驱动程序
 * <br>
 * 该程序接受词法单元串与 LR 分析表 (action 和 goto 表), 按表对词法单元流进行分析, 执行对应动作, 并在执行动作时通知各注册的观察者.
 * <br>
 * 你应当按照被挖空的方法的文档实现对应方法, 你可以随意为该类添加你需要的私有成员对象, 但不应该再为此类添加公有接口, 也不应该改动未被挖空的方法,
 * 除非你已经同助教充分沟通, 并能证明你的修改的合理性, 且令助教确定可能被改动的评测方法. 随意修改该类的其它部分有可能导致自动评测出错而被扣分.
 */
public class SyntaxAnalyzer {
    private final SymbolTable symbolTable;
    private final List<ActionObserver> observers = new ArrayList<>();
    private List<Token> tokens;
    private LRTable table;
    private List<Status> statusStack;
    private List<Term>   termsStack;


    public SyntaxAnalyzer(SymbolTable symbolTable) {

        this.symbolTable = symbolTable;
        // 初始化符号栈和状态栈 索引为0的位置栈底第一个元素
        this.statusStack = new ArrayList<Status>();
        this.termsStack = new ArrayList<Term>();
    }

    /**
     * 注册新的观察者
     *
     * @param observer 观察者
     */
    public void registerObserver(ActionObserver observer) {
        observers.add(observer);
        observer.setSymbolTable(symbolTable);
    }

    /**
     * 在执行 shift 动作时通知各个观察者
     *
     * @param currentStatus 当前状态
     * @param currentToken  当前词法单元
     */
    public void callWhenInShift(Status currentStatus, Token currentToken) {
        for (final var listener : observers) {
            listener.whenShift(currentStatus, currentToken);
        }
    }

    /**
     * 在执行 reduce 动作时通知各个观察者
     *
     * @param currentStatus 当前状态
     * @param production    待规约的产生式
     */
    public void callWhenInReduce(Status currentStatus, Production production) {
        for (final var listener : observers) {
            listener.whenReduce(currentStatus, production);
        }
    }

    /**
     * 在执行 accept 动作时通知各个观察者
     *
     * @param currentStatus 当前状态
     */
    public void callWhenInAccept(Status currentStatus) {
        for (final var listener : observers) {
            listener.whenAccept(currentStatus);
        }
    }

    public void loadTokens(Iterable<Token> tokens) {
        this.tokens = (List<Token>) tokens;
    }

    public void loadLRTable(LRTable table) {
        this.table = table;
//        System.out.println(this.table);
    }

    public void run() {
        // 初始化状态栈
        statusStack.add(table.getInit());
        // 语法分析是否成功接收
        boolean acc = false;
        while (!acc){
            // 查看剩余输入中的栈顶元素
            Token token = this.tokens.get(0);
            // 查看状态栈中的栈顶元素
            Status status = statusStack.get(statusStack.size()-1);
            // 查表获得当前行为
            Action action = table.getAction(status,token);

            switch (action.getKind()) {
                case Shift -> {
                    final var shiftTo = action.getStatus();

                    System.out.println("状态栈: "+ statusStack);
                    System.out.println("符号栈: "+ termsStack);
                    System.out.println(status + " " + token + " " + action);
                    System.out.println();

                    // 状态进栈、token进栈
                    statusStack.add(shiftTo);
                    termsStack.add(token.getKind());
                    callWhenInShift(status,token);

                    tokens.remove(0);

                }

                case Reduce -> {
                    final var production = action.getProduction();

                    System.out.println("状态栈: "+ statusStack);
                    System.out.println("符号栈: "+ termsStack);
                    System.out.println(status + " " + token + " " + action);
                    System.out.println();

                    // 符号栈和状态栈弹出和产生式右部符号个数相同的符号数
                    int size = production.body().size();
                    for(int i=1;i<=size;++i){
                        statusStack.remove(statusStack.size()-1);
                        termsStack.remove(termsStack.size()-1) ;
                    }
                    callWhenInReduce(status,production);

                    // 产生式右部符号进栈
                    termsStack.add(production.head());
                    // 查看状态栈顶状态
                    status = statusStack.get(statusStack.size()-1);
                    // 根据goto表转移到下一个状态
                    statusStack.add(table.getGoto(status,production.head())) ;

                }

                case Accept -> {
                    acc = true;
                    callWhenInAccept(status);
                    System.out.println("acc!");
                }

                case Error -> {
                    System.out.println("状态栈: "+ statusStack);
                    System.out.println("符号栈: "+ termsStack);
                    System.out.println(status + " " + token + " " + action);
                    System.out.println();

                    throw new RuntimeException("SyntaxError: invalid syntax! ");
                }

            }

        }

    }
}
